package merisis.impl;

import java.util.Arrays;
import java.util.HashMap;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReadWriteLock;
import java.util.concurrent.locks.ReentrantReadWriteLock;
import java.util.logging.Level;
import java.util.logging.Logger;

import org.apache.solr.util.OpenBitSet;

import com.google.inject.Inject;

import merisis.util.BitSet;
import merisis.util.BitSetIterator;
import merisis.util.BitUtil;
import merisis.util.OpenBitSetImpl;
import merisis.util.SortedArraySetUtil;

public class Hippocampus {

	private final HashMap<Long, BitSet> succedentMap = new HashMap<Long, BitSet>();
	private final HashMap<Long, BitSet> antecedentMap = new HashMap<Long, BitSet>();
	
	private final ReadWriteLock coneRWLock = new ReentrantReadWriteLock();
	private final Lock coneRLock = coneRWLock.readLock();
	private final Lock coneWLock = coneRWLock.writeLock();

	@Inject
	private Logger logger;

	public void occur(long s, long[] t) {

		BitSet ts = BitUtil.toBitSet(t);

		coneWLock.lock();
		try {

			if (antecedentMap.get(s) != null)
				throw new IllegalStateException("re-occured!!!");
			antecedentMap.put(s, ts);

			for (int i = 0; i < t.length; i++) {
				long k = t[i];

				BitSet v = succedentMap.get(k);
				if (v == null) {
					v = new OpenBitSetImpl();
				}
				v.set(s);
				succedentMap.put(k, v);
			}

		} finally {
			coneWLock.unlock();
		}
	}

	public void transit(long spindle, long s, BitSet ts) {

		coneWLock.lock();
		try {
			BitSet v;
			
			v = antecedentMap.get(s);
			if (v == null)
				throw new IllegalStateException();
			
			v.andNot(ts);
			if (!v.isEmpty()) {
				antecedentMap.put(s, v);
			} else {
				antecedentMap.remove(s);
			}
			
			BitSetIterator itr = ts.iterator();
			while(itr.hasNext()) {
				
				long k = itr.next();
				
				v = succedentMap.get(k);
				if (v == null)
					continue;
				
				v.clear(s);
				if (v.isEmpty()) {
					succedentMap.remove(k);
				} else {
					succedentMap.put(k, v);
				}
			}
		} finally {
			coneWLock.unlock();
		}
	}

	public long[] getSuccedents(long t) {

		BitSet buf;
		coneRLock.lock();
		try {
			buf = succedentMap.get(t);
		} finally {
			coneRLock.unlock();
		}
		if (buf == null)
			return new long[0];
		
		return buf.toLongArray();
	}

	public long[] getAntecedent(long s) {

		BitSet buf;
		coneRLock.lock();
		try {
			buf = antecedentMap.get(s);
		} finally {
			coneRLock.unlock();
		}

		if (buf == null)
			return new long[0];
		
		return buf.toLongArray();
	}

	public void dumpDebugInfo() {

		if (!logger.isLoggable(Level.FINEST))
			return;

		StringBuilder sb = new StringBuilder();
		sb.append("dump messages\n");
		OpenBitSet bs = new OpenBitSet();
		for (long k : antecedentMap.keySet()) {
			bs.set(k);
		}
		sb.append("Antecedents:\n");
		for (long k = bs.nextSetBit(0); k >= 0; k = bs.nextSetBit(k + 1)) {
			
			BitSet v = antecedentMap.get(k);
			sb.append(k);
			sb.append(": ");
			sb.append(v.toString());
			sb.append("\n");
		}

		bs = new OpenBitSet();
		for (long k : succedentMap.keySet()) {
			bs.set(k);
		}
		sb.append("Succedents:\n");
		for (long k = bs.nextSetBit(0); k >= 0; k = bs.nextSetBit(k + 1)) {
			BitSet v = succedentMap.get(k);
			sb.append(k);
			sb.append(": ");
			sb.append(v.toString());
			sb.append("\n");
		}

		logger.finest(sb.toString());
	}
}